/* File added by Espressif */

/**
 * lwip MDNS resolver file.
 *
 * Created on: Jul 29, 2010
 * Author: Daniel Toma
 *

 * ported from uIP resolv.c Copyright (c) 2002-2003, Adam Dunkels.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote
 *    products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**

 * This file implements a MDNS host name and PUCK service registration.

 *-----------------------------------------------------------------------------
 * Includes
 *----------------------------------------------------------------------------*/
#include "lwip/opt.h"
#if LWIP_MDNS /* don't build if not configured for use in lwipopts.h */
#include "lwip/mdns.h"
#include "lwip/puck_def.h"
#include "lwip/udp.h"
#include "lwip/mem.h"
#include "lwip/igmp.h"
#include "osapi.h"
#include "os_type.h"
#include "user_interface.h"

#ifdef MEMLEAK_DEBUG
static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__;
#endif

/** DNS server IP address */
#ifndef DNS_MULTICAST_ADDRESS
#define DNS_MULTICAST_ADDRESS \
  ipaddr_addr("224.0.0.251") /* resolver1.opendns.com */
#endif

/** DNS server IP address */
#ifndef MDNS_LOCAL
#define MDNS_LOCAL "local" /* resolver1.opendns.com */
#endif

/** DNS server port address */
#ifndef DNS_MDNS_PORT
#define DNS_MDNS_PORT 5353
#endif

/** DNS maximum number of retries when asking for a name, before "timeout". */
#ifndef DNS_MAX_RETRIES
#define DNS_MAX_RETRIES 4
#endif

/** DNS resource record max. TTL (one week as default) */
#ifndef DNS_MAX_TTL
#define DNS_MAX_TTL 604800
#endif

/* DNS protocol flags */
#define DNS_FLAG1_RESPONSE 0x84
#define DNS_FLAG1_OPCODE_STATUS 0x10
#define DNS_FLAG1_OPCODE_INVERSE 0x08
#define DNS_FLAG1_OPCODE_STANDARD 0x00
#define DNS_FLAG1_AUTHORATIVE 0x04
#define DNS_FLAG1_TRUNC 0x02
#define DNS_FLAG1_RD 0x01
#define DNS_FLAG2_RA 0x80
#define DNS_FLAG2_ERR_MASK 0x0f
#define DNS_FLAG2_ERR_NONE 0x00
#define DNS_FLAG2_ERR_NAME 0x03

/* DNS protocol states */
#define DNS_STATE_UNUSED 0
#define DNS_STATE_NEW 1
#define DNS_STATE_ASKING 2
#define DNS_STATE_DONE 3

/* MDNS registration type */
#define MDNS_HOSTNAME_REG 0
#define MDNS_SERVICE_REG 1

/* MDNS registration type */
#define MDNS_REG_ANSWER 1
#define MDNS_SD_ANSWER 2
#define MDNS_SERVICE_REG_ANSWER 3

/* MDNS registration time */
#define MDNS_HOST_TIME 120
#define MDNS_SERVICE_TIME 3600

/** MDNS name length with "." at the beginning and end of name*/
#ifndef MDNS_LENGTH_ADD
#define MDNS_LENGTH_ADD 2
#endif

#ifdef MDNS_MAX_NAME_LENGTH
#undef MDNS_MAX_NAME_LENGTH
#endif
#define MDNS_MAX_NAME_LENGTH (256)

PACK_STRUCT_BEGIN
/** DNS message header */
struct mdns_hdr {
  PACK_STRUCT_FIELD(u16_t id);
  PACK_STRUCT_FIELD(u8_t flags1);
  PACK_STRUCT_FIELD(u8_t flags2);
  PACK_STRUCT_FIELD(u16_t numquestions);
  PACK_STRUCT_FIELD(u16_t numanswers);
  PACK_STRUCT_FIELD(u16_t numauthrr);
  PACK_STRUCT_FIELD(u16_t numextrarr);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END

#define SIZEOF_DNS_HDR 12

PACK_STRUCT_BEGIN
/** MDNS query message structure */
struct mdns_query {
  /* MDNS query record starts with either a domain name or a pointer
	 to a name already present somewhere in the packet. */ PACK_STRUCT_FIELD(
      u16_t type);
  PACK_STRUCT_FIELD(u16_t class);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END

#define SIZEOF_DNS_QUERY 4

PACK_STRUCT_BEGIN
/** MDNS answer message structure */
struct mdns_answer {
  /* MDNS answer record starts with either a domain name or a pointer
	 to a name already present somewhere in the packet. */ PACK_STRUCT_FIELD(
      u16_t type);
  PACK_STRUCT_FIELD(u16_t class);
  PACK_STRUCT_FIELD(u32_t ttl);
  PACK_STRUCT_FIELD(u16_t len);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END
#define SIZEOF_DNS_ANSWER 10

PACK_STRUCT_BEGIN
/** MDNS answer message structure */
struct mdns_auth {
  PACK_STRUCT_FIELD(u32_t src);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END

#define SIZEOF_MDNS_AUTH 4
PACK_STRUCT_BEGIN
/** MDNS service registration message structure */
struct mdns_service {
  PACK_STRUCT_FIELD(u16_t prior);
  PACK_STRUCT_FIELD(u16_t weight);
  PACK_STRUCT_FIELD(u16_t port);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END

#define SIZEOF_MDNS_SERVICE 6

uint16 PUCK_PORT;
os_timer_t mdns_timer;
/* forward declarations */
static void mdns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p,
                      struct ip_addr *addr, u16_t port);

/*-----------------------------------------------------------------------------
 * Globales
 *----------------------------------------------------------------------------*/

/* MDNS variables */
static char host_name[MDNS_NAME_LENGTH];
static char service_name[MDNS_NAME_LENGTH];
static char server_name[MDNS_NAME_LENGTH];
// static char puck_datasheet[PUCK_DATASHEET_SIZE];
static struct udp_pcb *mdns_pcb = NULL;
static struct mdns_info *ms_info = NULL;
static struct ip_addr multicast_addr;
static struct ip_addr host_addr;
static uint8 register_flag = 0;
static uint8 mdns_flag = 0;
//#if (DNS_USES_STATIC_BUF == 1)
static u8_t mdns_payload[DNS_MSG_SIZE];
//#endif /* (MDNS_USES_STATIC_BUF == 1) */
/*
 *  Function to set the UDP pcb used to send the mDNS packages
 */
void ICACHE_FLASH_ATTR getPcb(struct udp_pcb *pcb) {
  mdns_pcb = pcb;
}

#if DNS_DOES_NAME_CHECK
/**
 * Compare the "dotted" name "query" with the encoded name "response"
 * to make sure an answer from the DNS server matches the current mdns_table
 * entry (otherwise, answers might arrive late for hostname not on the list
 * any more).
 *
 * @param query hostname (not encoded) from the mdns_table
 * @param response encoded hostname in the DNS response
 * @return 0: names equal; 1: names differ
 */
static u8_t ICACHE_FLASH_ATTR
mdns_compare_name(unsigned char *query, unsigned char *response) {
  unsigned char n;

  do {
    n = *response++;
    /** @see RFC 1035 - 4.1.4. Message compression */
    if ((n & 0xc0) == 0xc0) {
      /* Compressed name */
      break;
    } else {
      /* Not compressed name */
      while (n > 0) {
        if ((*query) != (*response)) {
          return 1;
        }
        ++response;
        ++query;
        --n;
      };
      ++query;
    }
  } while (*response != 0);

  return 0;
}
#endif /* DNS_DOES_NAME_CHECK */
       /**
        * Send a mDNS answer packet.
        *
        * @param type of answer hostname and service registration or service
        * @param name to query
        * @param id transaction ID in the DNS query packet
        * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise
        */
static err_t ICACHE_FLASH_ATTR
mdns_answer(u16_t type, const char *name, u8_t id) {
  err_t err;
  struct mdns_hdr *hdr;
  struct mdns_answer ans;
  struct mdns_auth auth;
  struct mdns_service serv;
  struct pbuf *p, *p_sta;
  char *query, *nptr;
  const char *pHostname;
  struct netif *sta_netif = NULL;
  struct netif *ap_netif = NULL;
  static char tmpBuf[PUCK_DATASHEET_SIZE + PUCK_SERVICE_LENGTH];
  u8_t n;
  u16_t length = 0;
  /* if here, we have either a new query or a retry on a previous query to
   * process */
  p = pbuf_alloc(PBUF_TRANSPORT,
                 SIZEOF_DNS_HDR + MDNS_MAX_NAME_LENGTH * 2 + SIZEOF_DNS_QUERY,
                 PBUF_RAM);
  if (p != NULL) {
    LWIP_ASSERT("pbuf must be in one piece", p->next == NULL);
    /* fill dns header */
    hdr = (struct mdns_hdr *) p->payload;
    os_memset(hdr, 0, SIZEOF_DNS_HDR);
    hdr->id = htons(id);
    hdr->flags1 = DNS_FLAG1_RESPONSE;

    if (type == MDNS_SD_ANSWER) {
      pHostname = DNS_SD_SERVICE;
      hdr->numanswers = htons(1);
    } else if (type == MDNS_SERVICE_REG_ANSWER) {
      pHostname = PUCK_SERVICE;
      hdr->numanswers = htons(type);
    } else {
      pHostname = name;
      hdr->numanswers = htons(type);
    }
    query = (char *) hdr + SIZEOF_DNS_HDR;
    --pHostname;
    /* convert hostname into suitable query format. */
    do {
      ++pHostname;
      nptr = query;
      ++query;
      for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
        *query = *pHostname;
        ++query;
        ++n;
      }
      *nptr = n;
    } while (*pHostname != 0);
    *query++ = '\0';
    /* fill dns query */

    if (type == MDNS_REG_ANSWER) {
      ans.type = htons(DNS_RRTYPE_A);
      ans.class = htons(DNS_RRCLASS_IN);
      ans.ttl = htonl(MDNS_SERVICE_TIME);
      ans.len = htons(DNS_IP_ADDR_LEN);
      length = DNS_IP_ADDR_LEN;

      MEMCPY(query, &ans, SIZEOF_DNS_ANSWER);

      /* resize the query */
      query = query + SIZEOF_DNS_ANSWER;

      /* set the local IP address */
      auth.src = host_addr.addr;
      MEMCPY(query, &auth, SIZEOF_MDNS_AUTH);
    }
    if (type == MDNS_SD_ANSWER) {
      ans.type = htons(DNS_RRTYPE_PTR);
      ans.class = htons(DNS_RRCLASS_IN);
      ans.ttl = htonl(300);
      ans.len = htons(os_strlen(PUCK_SERVICE) + 1 + 1);
      length = 0;

      MEMCPY(query, &ans, SIZEOF_DNS_ANSWER);

      /* resize the query */
      query = query + SIZEOF_DNS_ANSWER;
      pHostname = PUCK_SERVICE;
      --pHostname;

      /* convert hostname into suitable query format. */
      do {
        ++pHostname;
        nptr = query;
        ++query;
        for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
          *query = *pHostname;
          ++query;
          ++n;
        }
        *nptr = n;
      } while (*pHostname != 0);
      *query++ = '\0';
    }

    if (type == MDNS_SERVICE_REG_ANSWER) {
      ans.type = htons(DNS_RRTYPE_PTR);
      ans.class = htons(DNS_RRCLASS_IN);
      ans.ttl = htonl(MDNS_SERVICE_TIME);
      os_strcpy(tmpBuf, name);
      os_strcat(tmpBuf, ".");
      os_strcat(tmpBuf, PUCK_SERVICE);

      length = os_strlen(tmpBuf) + MDNS_LENGTH_ADD;
      ans.len = htons(length);
      length = 0;

      MEMCPY(query, &ans, SIZEOF_DNS_ANSWER);

      /* resize the query */
      query = query + SIZEOF_DNS_ANSWER;

      pHostname = tmpBuf;
      --pHostname;

      /* convert hostname into suitable query format. */
      do {
        ++pHostname;
        nptr = query;
        ++query;
        for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
          *query = *pHostname;
          ++query;
          ++n;
        }
        *nptr = n;
      } while (*pHostname != 0);
      *query++ = '\0';

      /* Service query*/
      pHostname = name;
      --pHostname;

      /* convert hostname into suitable query format. */
      do {
        ++pHostname;
        nptr = query;
        ++query;
        for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
          *query = *pHostname;
          ++query;
          ++n;
        }
        *nptr = n;
      } while (*pHostname != 0);

      /* Add to the service name the service local
       * pointing to the beginning of the mDNS message*/
      *query++ = DNS_OFFSET_FLAG;
      *query++ = DNS_DEFAULT_OFFSET;

      /* fill the query */

      ans.type = htons(DNS_RRTYPE_SRV);
      ans.class = htons(DNS_RRCLASS_FLUSH_IN);
      ans.ttl = htonl(MDNS_SERVICE_TIME);
      os_strcpy(tmpBuf, host_name);
      os_strcat(tmpBuf, ".");
      os_strcat(tmpBuf, MDNS_LOCAL);
      length = os_strlen(tmpBuf) + MDNS_LENGTH_ADD;
      ans.len = htons(SIZEOF_MDNS_SERVICE + length);
      length = 0;
      MEMCPY(query, &ans, SIZEOF_DNS_ANSWER);

      /* resize the query */
      query = query + SIZEOF_DNS_ANSWER;
      /* fill the service properties */

      serv.prior = htons(0);
      serv.weight = htons(0);
      serv.port = htons(PUCK_PORT);
      MEMCPY(query, &serv, SIZEOF_MDNS_SERVICE);
      /* resize the query */
      query = query + SIZEOF_MDNS_SERVICE;

      pHostname = tmpBuf;
      --pHostname;

      /* convert hostname into suitable query format. */
      do {
        ++pHostname;
        nptr = query;
        ++query;
        for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
          *query = *pHostname;
          ++query;
          ++n;
        }
        *nptr = n;
      } while (*pHostname != 0);
      *query++ = '\0';

      /* TXT answer */
      pHostname = name;
      --pHostname;

      /* convert hostname into suitable query format. */
      do {
        ++pHostname;
        nptr = query;
        ++query;
        for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
          *query = *pHostname;
          ++query;
          ++n;
        }
        *nptr = n;
      } while (*pHostname != 0);

      /* Add to the service name the service local
       * pointing to the beginning of the mDNS message*/
      *query++ = DNS_OFFSET_FLAG;
      *query++ = DNS_DEFAULT_OFFSET;

      /* fill the answer */
      ans.type = htons(DNS_RRTYPE_TXT);
      ans.class = htons(DNS_RRCLASS_IN);
      ans.ttl = htonl(MDNS_SERVICE_TIME);
      length = sizeof(SERVICE_DESCRIPTION);
      ans.len = htons(length);
      length = 0;
      MEMCPY(query, &ans, SIZEOF_DNS_ANSWER);

      /* resize the query */
      query = query + SIZEOF_DNS_ANSWER;

      pHostname = SERVICE_DESCRIPTION;
      --pHostname;

      /* convert hostname into suitable query format. */
      do {
        ++pHostname;
        nptr = query;
        ++query;
        for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
          *query = *pHostname;
          ++query;
          ++n;
        }
        *nptr = n;
      } while (*pHostname != 0);
      *query++ = '\0';
    }
    /* resize pbuf to the exact dns query */
    pbuf_realloc(p, (query + length) - ((char *) (p->payload)));

    /* send dns packet */
    /*add by tzx for AP + STA MDNS begin------*/
    sta_netif = (struct netif *) eagle_lwip_getif(0x00);
    ap_netif = (struct netif *) eagle_lwip_getif(0x01);
    if (wifi_get_opmode() == 0x03 && wifi_get_broadcast_if() == 0x03 &&
        sta_netif != NULL && ap_netif != NULL) {
      if (netif_is_up(sta_netif) && netif_is_up(ap_netif)) {
        p_sta = pbuf_alloc(
            PBUF_TRANSPORT,
            SIZEOF_DNS_HDR + MDNS_MAX_NAME_LENGTH * 2 + SIZEOF_DNS_QUERY,
            PBUF_RAM);
        if (pbuf_copy(p_sta, p) != ERR_OK) {
          os_printf("mdns_answer copying to new pbuf failed\n");
          return -1;
        }
        netif_set_default(sta_netif);
        err = udp_sendto(mdns_pcb, p_sta, &multicast_addr, DNS_MDNS_PORT);
        pbuf_free(p_sta);
        netif_set_default(ap_netif);
      }
    }
    /*add by tzx for AP + STA MDNS end------*/
    err = udp_sendto(mdns_pcb, p, &multicast_addr, DNS_MDNS_PORT);
    /* free pbuf */
    pbuf_free(p);
  } else {
    err = ERR_MEM;
  }

  return err;
}

/**
 * Send a mDNS service answer packet.
 *
 * @param name service name to query
 * @param id transaction ID in the DNS query packet
 * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise
 */
static err_t ICACHE_FLASH_ATTR
mdns_send_service(struct mdns_info *info, u8_t id) {
  err_t err;
  struct mdns_hdr *hdr;
  struct mdns_answer ans;
  struct mdns_service serv;
  struct mdns_auth auth;
  struct pbuf *p, *p_sta;
  char *query, *nptr;
  const char *pHostname;
  char *device_info;
  const char *name = info->host_name;
  u8_t n;
  u8_t i = 0;
  u16_t length = 0;
  u8_t addr1 = 12, addr2 = 12;
  struct netif *sta_netif = NULL;
  struct netif *ap_netif = NULL;
  static char tmpBuf[PUCK_DATASHEET_SIZE + PUCK_SERVICE_LENGTH];
  /* if here, we have either a new query or a retry on a previous query to
   * process */
  p = pbuf_alloc(PBUF_TRANSPORT,
                 SIZEOF_DNS_HDR + MDNS_MAX_NAME_LENGTH * 2 + SIZEOF_DNS_QUERY,
                 PBUF_RAM);
  if (p != NULL) {
    LWIP_ASSERT("pbuf must be in one piece", p->next == NULL);
    /* fill dns header */
    hdr = (struct mdns_hdr *) p->payload;
    os_memset(hdr, 0, SIZEOF_DNS_HDR);
    hdr->id = htons(id);
    hdr->flags1 = DNS_FLAG1_RESPONSE;
    hdr->numanswers = htons(4);
    query = (char *) hdr + SIZEOF_DNS_HDR;
    os_strcpy(tmpBuf, PUCK_SERVICE);

    pHostname = tmpBuf;
    --pHostname;

    /* convert hostname into suitable query format. */
    do {
      ++pHostname;
      nptr = query;
      ++query;
      ++addr1;
      ++addr2;
      for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
        *query = *pHostname;
        ++query;
        ++addr1;
        ++addr2;
        ++n;
      }
      *nptr = n;
    } while (*pHostname != 0);
    *query++ = '\0';
    length = sizeof(MDNS_LOCAL);
    addr1 -= length;
    length = os_strlen(PUCK_SERVICE) + 1;
    addr2 -= length;

    ans.type = htons(DNS_RRTYPE_PTR);
    ans.class = htons(DNS_RRCLASS_IN);
    ans.ttl = htonl(300);
    os_strcpy(tmpBuf, name);
    length = os_strlen(tmpBuf) + MDNS_LENGTH_ADD + 1;
    ans.len = htons(length);
    length = 0;

    MEMCPY(query, &ans, SIZEOF_DNS_ANSWER);
    /* resize the query */
    query = query + SIZEOF_DNS_ANSWER;

    pHostname = tmpBuf;
    --pHostname;
    /* convert hostname into suitable query format. */
    do {
      ++pHostname;
      nptr = query;
      ++query;
      for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
        *query = *pHostname;
        ++query;
        ++n;
      }
      *nptr = n;
    } while (*pHostname != 0);
    *query++ = DNS_OFFSET_FLAG;
    *query++ = DNS_DEFAULT_OFFSET;
    pHostname = name;
    --pHostname;
    /* convert hostname into suitable query format. */
    do {
      ++pHostname;
      nptr = query;
      ++query;
      for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
        *query = *pHostname;
        ++query;
        ++n;
      }
      *nptr = n;
    } while (*pHostname != 0);
    //*query++ = '\0';
    *query++ = DNS_OFFSET_FLAG;
    *query++ = DNS_DEFAULT_OFFSET;

    /* fill the answer */
    ans.type = htons(DNS_RRTYPE_TXT);
    ans.class = htons(DNS_RRCLASS_FLUSH_IN);
    ans.ttl = htonl(300);
    //		length = os_strlen(TXT_DATA) + MDNS_LENGTH_ADD + 1;
    device_info = (char *) os_zalloc(50);
    ets_sprintf(device_info, "vendor = %s", "Espressif");
    for (i = 0; i < 10 && (info->txt_data[i] != NULL); i++) {
      length += os_strlen(info->txt_data[i]);
      length++;
    }
    length += os_strlen(device_info) + 1;
    ans.len = htons(length);
    length = 0;
    MEMCPY(query, &ans, SIZEOF_DNS_ANSWER);
    query = query + SIZEOF_DNS_ANSWER;
    pHostname = device_info;
    --pHostname;
    /* convert hostname into suitable query format. */
    do {
      ++pHostname;
      nptr = query;
      ++query;
      for (n = 0; *pHostname != 0; ++pHostname) {
        *query = *pHostname;
        ++query;
        ++n;
      }
      *nptr = n;
    } while (*pHostname != 0);
    i = 0;
    while (info->txt_data[i] != NULL && i < 10) {
      pHostname = info->txt_data[i];
      --pHostname;
      /* convert hostname into suitable query format. */
      do {
        ++pHostname;
        nptr = query;
        ++query;
        for (n = 0; *pHostname != 0; ++pHostname) {
          *query = *pHostname;
          ++query;
          ++n;
        }
        *nptr = n;
      } while (*pHostname != 0);
      i++;
    }
    //		*query++ = '\0';
    os_free(device_info);
    os_strcpy(tmpBuf, name);
    pHostname = tmpBuf;
    --pHostname;
    do {
      ++pHostname;
      nptr = query;
      ++query;
      for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
        *query = *pHostname;
        ++query;
        ++n;
      }
      *nptr = n;
    } while (*pHostname != 0);

    *query++ = DNS_OFFSET_FLAG;
    *query++ = DNS_DEFAULT_OFFSET;

    ans.type = htons(DNS_RRTYPE_SRV);
    ans.class = htons(DNS_RRCLASS_FLUSH_IN);
    ans.ttl = htonl(300);
    os_strcpy(tmpBuf, service_name);
    os_strcat(tmpBuf, ".");
    os_strcat(tmpBuf, MDNS_LOCAL);
    length = os_strlen(tmpBuf) + MDNS_LENGTH_ADD;
    ans.len = htons(SIZEOF_MDNS_SERVICE + length);
    length = 0;
    MEMCPY(query, &ans, SIZEOF_DNS_ANSWER);

    /* resize the query */
    query = query + SIZEOF_DNS_ANSWER;

    serv.prior = htons(0);
    serv.weight = htons(0);
    serv.port = htons(PUCK_PORT);
    MEMCPY(query, &serv, SIZEOF_MDNS_SERVICE);
    /* resize the query */
    query = query + SIZEOF_MDNS_SERVICE;

    pHostname = tmpBuf;
    --pHostname;
    do {
      ++pHostname;
      nptr = query;
      ++query;
      for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
        *query = *pHostname;
        ++query;
        ++n;
      }
      *nptr = n;
    } while (*pHostname != 0);
    *query++ = '\0';
    /* set the name of the authority field.
     * The same name as the Query using the offset address*/
    os_strcpy(tmpBuf, service_name);
    os_strcat(tmpBuf, ".");
    os_strcat(tmpBuf, MDNS_LOCAL);
    pHostname = tmpBuf;
    --pHostname;
    do {
      ++pHostname;
      nptr = query;
      ++query;
      for (n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
        *query = *pHostname;
        ++query;
        ++n;
      }
      *nptr = n;
    } while (*pHostname != 0);
    *query++ = '\0';
    /* set the name of the authority field.
     * The same name as the Query using the offset address*/
    //*query++ = DNS_OFFSET_FLAG;
    //*query++ = DNS_DEFAULT_OFFSET;
    ans.type = htons(DNS_RRTYPE_A);
    ans.class = htons(DNS_RRCLASS_FLUSH_IN);
    ans.ttl = htonl(300);
    ans.len = htons(DNS_IP_ADDR_LEN);

    MEMCPY(query, &ans, SIZEOF_DNS_ANSWER);

    /* resize the query */
    query = query + SIZEOF_DNS_ANSWER;

    /* fill the payload of the mDNS message */
    /* set the local IP address */
    auth.src = host_addr.addr;  // ipAddr;
    MEMCPY(query, &auth, SIZEOF_MDNS_AUTH);
    /* resize the query */
    query = query + SIZEOF_MDNS_AUTH;

    /* set the name of the authority field.
     * The same name as the Query using the offset address*/

    /* resize pbuf to the exact dns query */
    pbuf_realloc(p, (query) - ((char *) (p->payload)));
    /* send dns packet */
    sta_netif = (struct netif *) eagle_lwip_getif(0x00);
    ap_netif = (struct netif *) eagle_lwip_getif(0x01);
    if (wifi_get_opmode() == 0x03 && wifi_get_broadcast_if() == 0x03 &&
        sta_netif != NULL && ap_netif != NULL) {
      if (netif_is_up(sta_netif) && netif_is_up(ap_netif)) {
        p_sta = pbuf_alloc(
            PBUF_TRANSPORT,
            SIZEOF_DNS_HDR + MDNS_MAX_NAME_LENGTH * 2 + SIZEOF_DNS_QUERY,
            PBUF_RAM);
        if (pbuf_copy(p_sta, p) != ERR_OK) {
          os_printf("mdns_send_service copying to new pbuf failed\n");
          return -1;
        }
        netif_set_default(sta_netif);
        err = udp_sendto(mdns_pcb, p_sta, &multicast_addr, DNS_MDNS_PORT);
        pbuf_free(p_sta);
        netif_set_default(ap_netif);
      }
    }
    err = udp_sendto(mdns_pcb, p, &multicast_addr, DNS_MDNS_PORT);

    /* free pbuf */
    pbuf_free(p);
  } else {
    os_printf("ERR_MEM \n");
    err = ERR_MEM;
  }

  return err;
}

/**
 * Receive input function for DNS response packets arriving for the dns UDP pcb.
 *
 * @params see udp.h
 */
static void ICACHE_FLASH_ATTR mdns_recv(void *arg, struct udp_pcb *pcb,
                                        struct pbuf *p, struct ip_addr *addr,
                                        u16_t port) {
  u8_t i;
  struct mdns_hdr *hdr;
  u8_t nquestions;
  LWIP_UNUSED_ARG(arg);
  LWIP_UNUSED_ARG(pcb);
  LWIP_UNUSED_ARG(addr);
  LWIP_UNUSED_ARG(port);
  struct mdns_info *info = (struct mdns_info *) arg;
  /* is the dns message too big ? */
  if (p->tot_len > DNS_MSG_SIZE) {
    LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too big\n"));
    /* free pbuf and return */
    goto memerr1;
  }

  /* is the dns message big enough ? */
  if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY + SIZEOF_DNS_ANSWER)) {
    LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n"));
    /* free pbuf and return */
    goto memerr1;
  }
  /* copy dns payload inside static buffer for processing */
  if (pbuf_copy_partial(p, mdns_payload, p->tot_len, 0) == p->tot_len) {
    /* The ID in the DNS header should be our entry into the name table. */
    hdr = (struct mdns_hdr *) mdns_payload;

    i = htons(hdr->id);
    if (i < DNS_TABLE_SIZE) {
      nquestions = htons(hdr->numquestions);
      // nanswers   = htons(hdr->numanswers);
      /* if we have a question send an answer if necessary */
      if (nquestions > 0) {
        /* MDNS_DS_DOES_NAME_CHECK */
        /* Check if the name in the "question" part match with the name of the
         * MDNS DS service. */
        if (mdns_compare_name(
                (unsigned char *) DNS_SD_SERVICE,
                (unsigned char *) mdns_payload + SIZEOF_DNS_HDR) == 0) {
          /* respond with the puck service*/
          mdns_answer(MDNS_SD_ANSWER, PUCK_SERVICE, 0);
        } else if (mdns_compare_name(
                       (unsigned char *) PUCK_SERVICE,
                       (unsigned char *) mdns_payload + SIZEOF_DNS_HDR) == 0) {
          /* respond with the puck service*/
          mdns_send_service(info, 0);
        } else
          goto memerr2;
      }
    }
  }
  goto memerr2;
memerr2:
  os_memset(mdns_payload, 0, DNS_MSG_SIZE);
memerr1:
  /* free pbuf */
  pbuf_free(p);
  return;
}

/**
 * close the UDP pcb .
 */
void ICACHE_FLASH_ATTR mdns_close(void) {
  uint8 text_index = 0;
  if (mdns_pcb != NULL && ms_info != NULL) {
    udp_remove(mdns_pcb);
    for (text_index = 0; text_index < 10; text_index++) {
      if (ms_info->txt_data[text_index] != NULL) {
        os_free(ms_info->txt_data[text_index]);
        ms_info->txt_data[text_index] = NULL;
      }
    }
    if (ms_info->host_name != NULL) {
      os_free(ms_info->host_name);
      ms_info->host_name = NULL;
    }
    if (ms_info->server_name != NULL) {
      os_free(ms_info->server_name);
      ms_info->server_name = NULL;
    }
    os_free(ms_info);
    mdns_pcb = NULL;
    ms_info = NULL;
  }
}

void ICACHE_FLASH_ATTR mdns_set_name(const char *name) {
  // strcpy(host_name, name);
  os_strcpy(service_name, name);
}

void ICACHE_FLASH_ATTR mdns_enable(void) {
  if (mdns_flag == 0) {
    udp_recv(mdns_pcb, mdns_recv, NULL);
  }
}

void ICACHE_FLASH_ATTR mdns_disable(void) {
  if (mdns_flag == 1) {
    udp_recv(mdns_pcb, NULL, NULL);
  }
}

/**
 * close the UDP pcb .
 */
char *ICACHE_FLASH_ATTR mdns_get_hostname(void) {
  // strcpy(host_name, name);
  char *name = host_name;
  if (host_name[0] != 0) {
    return name;
  } else {
    return ("Espressif");
  }
}

void ICACHE_FLASH_ATTR mdns_set_hostname(char *name) {
  if (name == NULL) {
    os_strncpy(host_name, "Espressif", os_strlen("Espressif") + 3);
    return;
  }
  if (os_strlen(name) + 3 <= MDNS_NAME_LENGTH) {
    os_strncpy(host_name, name, os_strlen(name));
    //		os_memset(host_name + os_strlen(host_name) ,0x00,3);
  } else {
    os_strncpy(host_name, name, MDNS_NAME_LENGTH);
  }
}

void ICACHE_FLASH_ATTR mdns_set_servername(const char *name) {
  if (name == NULL) {
    PUCK_SERVICE = "_Espressif._tcp._local";
  } else {
    os_sprintf(server_name, "_%s._tcp.local", name);
    PUCK_SERVICE = server_name;
  }
}

char *ICACHE_FLASH_ATTR mdns_get_servername(void) {
  char *name = PUCK_SERVICE;
  if (name == NULL) {
    PUCK_SERVICE = "_Espressif._tcp._local";
  }
  return name;
}

void ICACHE_FLASH_ATTR mdns_server_unregister(void) {
  struct ip_addr ap_host_addr;
  struct ip_info ipconfig;
  if (register_flag == 1) {
    if (igmp_leavegroup(&host_addr, &multicast_addr) != ERR_OK) {
      os_printf("sta udp_leave_multigrup failed!\n");
      return;
    };
    if (wifi_get_opmode() == 0x03 || wifi_get_opmode() == 0x02) {
      wifi_get_ip_info(SOFTAP_IF, &ipconfig);
      ap_host_addr.addr = ipconfig.ip.addr;
      if (igmp_leavegroup(&ap_host_addr, &multicast_addr) != ERR_OK) {
        os_printf("ap udp_join_multigrup failed!\n");
        return;
      };
    }
    register_flag = 0;
  }
}

void ICACHE_FLASH_ATTR mdns_server_register(void) {
  if (register_flag == 1) {
    os_printf("mdns server is already registered !\n");
    return;
  } else if (igmp_joingroup(&host_addr, &multicast_addr) != ERR_OK) {
    os_printf("udp_join_multigrup failed!\n");
    return;
  };
  register_flag = 1;
}

void ICACHE_FLASH_ATTR mdns_reg(struct mdns_info *info) {
  static uint8 i = 0;
  if (i <= 3) {
    mdns_send_service(info, 0);
    i++;
  } else {
    os_timer_disarm(&mdns_timer);
  }
}

/**
 * Initialize the resolver: set up the UDP pcb and configure the default server
 * (NEW IP).
 */
void ICACHE_FLASH_ATTR mdns_init(struct mdns_info *info) {
  /* initialize default DNS server address */
  multicast_addr.addr = DNS_MULTICAST_ADDRESS;
  struct ip_addr ap_host_addr;
  struct ip_info ipconfig;
  uint8 text_index = 0;
  ms_info = (struct mdns_info *) os_zalloc(sizeof(struct mdns_info));
  if (ms_info != NULL) {
    os_memcpy(ms_info, info, sizeof(struct mdns_info));
    ms_info->host_name = (char *) os_zalloc(os_strlen(info->host_name) + 1);
    os_memcpy(ms_info->host_name, info->host_name, os_strlen(info->host_name));
    ms_info->server_name = (char *) os_zalloc(os_strlen(info->server_name) + 1);
    os_memcpy(ms_info->server_name, info->server_name,
              os_strlen(info->server_name));
    for (text_index = 0; text_index < 10; text_index++) {
      if (info->txt_data[text_index] != NULL) {
        ms_info->txt_data[text_index] =
            (char *) os_zalloc(os_strlen(info->txt_data[text_index]) + 1);
        os_memcpy(ms_info->txt_data[text_index], info->txt_data[text_index],
                  os_strlen(info->txt_data[text_index]));
      } else {
        break;
      }
    }

  } else {
    os_printf("ms_info alloc failed\n");
    return;
  }
  if (ms_info->ipAddr == 0) {
    os_printf("mdns ip error!\n ");
    return;
  }
  host_addr.addr = ms_info->ipAddr;
  LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n"));
  // get the datasheet from PUCK
  mdns_set_hostname(ms_info->host_name);
  mdns_set_servername(ms_info->server_name);
  mdns_set_name(ms_info->host_name);

  // get the host name as instrumentName_serialNumber for MDNS
  // set the name of the service, the same as host name
  os_printf("host_name = %s\n", host_name);
  os_printf("server_name = %s\n", PUCK_SERVICE);
  if (ms_info->server_port == 0) {
    PUCK_PORT = 80;
  } else {
    PUCK_PORT = ms_info->server_port;
  }

  /* initialize mDNS */
  mdns_pcb = udp_new();

  if (mdns_pcb != NULL) {
    /* join to the multicast address 224.0.0.251 */
    if (wifi_get_opmode() == 0x03 || wifi_get_opmode() == 0x01) {
      if (igmp_joingroup(&host_addr, &multicast_addr) != ERR_OK) {
        os_printf("sta udp_join_multigrup failed!\n");
        return;
      };
    }
    if (wifi_get_opmode() == 0x03 || wifi_get_opmode() == 0x02) {
      wifi_get_ip_info(SOFTAP_IF, &ipconfig);
      ap_host_addr.addr = ipconfig.ip.addr;
      if (igmp_joingroup(&ap_host_addr, &multicast_addr) != ERR_OK) {
        os_printf("ap udp_join_multigrup failed!\n");
        return;
      };
    }
    register_flag = 1;
    /* join to any IP address at the port 5353 */
    if (udp_bind(mdns_pcb, IP_ADDR_ANY, DNS_MDNS_PORT) != ERR_OK) {
      os_printf("udp_bind failed!\n");
      return;
    };

    /*loopback function for the multicast(224.0.0.251) messages received at port
     * 5353*/
    //		mdns_enable();
    udp_recv(mdns_pcb, mdns_recv, ms_info);
    mdns_flag = 1;
    /*
     * Register the name of the instrument
     */

    os_timer_disarm(&mdns_timer);
    os_timer_setfn(&mdns_timer, (os_timer_func_t *) mdns_reg, ms_info);
    os_timer_arm(&mdns_timer, 1000, 1);
  }
}

#endif /* LWIP_MDNS */
